package com.megvii.koala.camera;

import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;
import android.widget.RelativeLayout;

import com.megvii.corelib.util.Screen;
import com.megvii.koala.util.MatrixUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Created by linyue on 16/1/2.
 */

/**
 * 写死了Preview用NV21格式
 */
public class CameraManager implements CameraPreview.CameraPreviewListener, Camera.FaceDetectionListener {
    protected boolean front = false;

    protected Camera camera = null;

    protected int cameraId = -1;

    protected SurfaceHolder surfaceHolder = null;

    private CameraListener listener = null;

    private boolean takingPicture = false;

    private boolean continuousPreview = false;

    private CameraPreview cameraPreview;

    private CameraState state = CameraState.IDEL;

    private AsyncTask<Object, Object, Object> openTask = null;

    private Matrix mMatrix = new Matrix();

    private int width, height;

    private int previewDegreen = 0;

    public CameraManager(boolean continuousPreview) {
        super();
        this.continuousPreview = continuousPreview;
    }

    private static Camera.Size getBestPreviewSize(Camera mCamera) {
        Camera.Parameters camPara = mCamera.getParameters();
        List<Camera.Size> allSupportedSize = camPara.getSupportedPreviewSizes();
        ArrayList<Camera.Size> widthLargerSize = new ArrayList<Camera.Size>();
        int max = Integer.MIN_VALUE;
        Camera.Size maxSize = null;
        for (Camera.Size tmpSize : allSupportedSize) {
            int multi = tmpSize.height * tmpSize.width;
            if (multi > max) {
                max = multi;
                maxSize = tmpSize;
            }
            //选分辨率比较高的
            if (tmpSize.width > tmpSize.height && (tmpSize.width > Screen.mHeight / 2 || tmpSize.height > Screen.mWidth / 2)) {
                widthLargerSize.add(tmpSize);
            }
        }
        if (widthLargerSize.isEmpty()) {
            widthLargerSize.add(maxSize);
        }

        final float propotion = (float) Screen.mHeight / (float) Screen.mWidth;

        Collections.sort(widthLargerSize, new Comparator<Camera.Size>() {
            @Override
            public int compare(Camera.Size lhs, Camera.Size rhs) {
                //                                int off_one = Math.abs(lhs.width * lhs.height - Screen.mWidth * Screen.mHeight);
                //                                int off_two = Math.abs(rhs.width * rhs.height - Screen.mWidth * Screen.mHeight);
                //                                return off_one - off_two;
                //选预览比例跟屏幕比例比较接近的
                float a = getPropotionDiff(lhs, propotion);
                float b = getPropotionDiff(rhs, propotion);
                return (int) ((a - b) * 10000);
            }
        });

        float minPropotionDiff = getPropotionDiff(widthLargerSize.get(0), propotion);
        ArrayList<Camera.Size> validSizes = new ArrayList<>();
        for (int i = 0; i < widthLargerSize.size(); i++) {
            Camera.Size size = widthLargerSize.get(i);
            float propotionDiff = getPropotionDiff(size, propotion);
            if (propotionDiff > minPropotionDiff) {
                break;
            }
            validSizes.add(size);
        }

        Collections.sort(validSizes, new Comparator<Camera.Size>() {
            @Override
            public int compare(Camera.Size lhs, Camera.Size rhs) {
                return rhs.width * rhs.height - lhs.width * lhs.height;
            }
        });
        return validSizes.get(0);
    }

    public static float getPropotionDiff(Camera.Size size, float standardPropotion) {
        return Math.abs((float) size.width / (float) size.height - standardPropotion);
    }

    /**
     * 获取展示屏幕的宽高
     */
    public RelativeLayout.LayoutParams getParams(Camera.Size bestPreviewSize) {
        // 因为旋转90度所以bestPreviewSize.height对应屏幕的宽
        float scaleW = (float) ((Screen.mWidth * 1.00) / (bestPreviewSize.height * 1.00));
        float scaleH = (float) ((Screen.mHeight * 1.00) / (bestPreviewSize.width * 1.00));
        float scale = scaleW > scaleH ? scaleH : scaleW;

        RelativeLayout.LayoutParams layoutPara = new RelativeLayout.LayoutParams(
                (int) (bestPreviewSize.height * scale), (int) (bestPreviewSize.width * scale));

        layoutPara.addRule(RelativeLayout.CENTER_HORIZONTAL);// 设置照相机水平居中
        return layoutPara;
    }

    public boolean open(final WindowManager windowManager) {
        if (state != CameraState.OPENING) {
            state = CameraState.OPENING;
            release();
            new AsyncTask<Object, Object, Object>() {
                @Override
                protected Object doInBackground(Object... params) {
                    cameraId = front ? Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
                    try {
                        camera = Camera.open(cameraId);
                    } catch (Exception e) {
                        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
                        int count = Camera.getNumberOfCameras();
                        if (count > 0) {
                            cameraId = 0;
                            camera = Camera.open(cameraId);
                        } else {
                            cameraId = -1;
                            camera = null;
                        }
                    }
                    if (camera != null) {
                        Camera.CameraInfo info = new Camera.CameraInfo();
                        Camera.getCameraInfo(cameraId, info);
                        int rotation = windowManager.getDefaultDisplay().getRotation();
                        int degrees = 0;
                        switch (rotation) {
                            case Surface.ROTATION_0:
                                degrees = 0;
                                break;
                            case Surface.ROTATION_90:
                                degrees = 90;
                                break;
                            case Surface.ROTATION_180:
                                degrees = 180;
                                break;
                            case Surface.ROTATION_270:
                                degrees = 270;
                                break;
                        }
                        int previewRotation;
                        int takePictureRotation;
                        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                            takePictureRotation = (info.orientation + degrees) % 360;
                            previewRotation = (360 - takePictureRotation) % 360;  // compensate the mirror
                        } else {  // back-facing
                            previewRotation = (info.orientation - degrees + 360) % 360;
                            takePictureRotation = previewRotation;
                        }
                        previewDegreen = previewRotation;
                        camera.setDisplayOrientation(previewRotation);
                        MatrixUtil.prepareMatrix(mMatrix, true, previewRotation, width, height);
                        camera.setFaceDetectionListener(CameraManager.this);
                        Camera.Parameters param = camera.getParameters();
//                        param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
//                        param.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
                        param.setRotation(takePictureRotation);
                        Camera.Size bestPreviewSize = getBestPreviewSize(camera);
                        param.setPreviewSize(bestPreviewSize.width, bestPreviewSize.height);
                        //TODO 如果摄像头不支持这个怎么办？
                        param.setPreviewFormat(ImageFormat.NV21);
                        camera.setParameters(param);
                        //
                        try {
                            camera.setPreviewDisplay(surfaceHolder);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    return null;
                }

                @Override
                protected void onPostExecute(Object o) {
                    super.onPostExecute(o);
                    cameraPreview.setCamera(camera);
                    state = CameraState.OPENED;
                    openTask = null;
                }
            }.execute();
            return true;
        } else {
            return false;
        }
    }

    public boolean open(WindowManager windowManager, boolean front) {
        if (state == CameraState.OPENING) {
            return false;
        }
        this.front = front;
        return open(windowManager);
    }

    public boolean toggleCamera(WindowManager windowManager) {
        if (state == CameraState.OPENING) {
            return false;
        }
        this.front = !this.front;
        return open(windowManager);
    }

    public int getCameraId() {
        if (front) {
            return Camera.CameraInfo.CAMERA_FACING_FRONT;
        } else {
            return Camera.CameraInfo.CAMERA_FACING_BACK;
        }
    }

    public void release() {
        if (camera != null) {
            camera.stopPreview();
            camera.setPreviewCallback(null);
            camera.stopFaceDetection();
            camera.release();
            camera = null;
            takingPicture = false;
            if (openTask != null) {
                openTask.cancel(true);
            }
        }
    }

    public void setPreviewDisplay(CameraPreview preview) {
        this.cameraPreview = preview;
        this.surfaceHolder = preview.getHolder();
        width = preview.getMeasuredWidth();
        height = preview.getMeasuredHeight();
        preview.setListener(this);
    }

    public boolean takePicture() {
        if (state != CameraState.OPENED) {
            return false;
        }
        if (continuousPreview) {
            return false;
        }
        if (!takingPicture) {
            takingPicture = true;
            camera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    if (listener != null) {
                        camera.setPreviewCallback(null);
                        Camera.Size previewSize = camera.getParameters().getPreviewSize();
                        Camera.CameraInfo info = new Camera.CameraInfo();
                        Camera.getCameraInfo(getCameraId(), info);
                        listener.onPictureTaken(
                                new CameraPreviewData(data, previewSize.width, previewSize.height,
                                        info.orientation, front));
                    }
                    takingPicture = false;
                }
            });
        }
        return true;
    }

    public void setListener(CameraListener listener) {
        this.listener = listener;
    }

    @Override
    public void onStartPreview() {
        if (continuousPreview) {
            camera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    if (listener != null) {
                        Camera.Size previewSize = camera.getParameters().getPreviewSize();
                        listener.onPictureTaken(
                                new CameraPreviewData(data, previewSize.width, previewSize.height,
                                        previewDegreen, front));
                        //
                        camera.addCallbackBuffer(data);
                    }
                }
            });
        }
    }

    @Override
    public void onFaceDetection(Camera.Face[] faces, Camera camera) {
        if (listener != null) {
            RectF rectF;
            for (Camera.Face face :
                    faces) {
                rectF = new RectF(face.rect);
                mMatrix.mapRect(rectF);
                face.rect.set((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom);
            }
            listener.onFaceDetected(faces, camera);
        }
    }

    public enum CameraState {
        IDEL,
        OPENING,
        OPENED
    }

    public interface CameraListener {
        void onPictureTaken(CameraPreviewData cameraPreviewData);

        void onFaceDetected(Camera.Face[] faces, Camera camera);
    }
}
